package com.nsoft.pdfrender;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import nl.siegmann.epublib.domain.Author;
import nl.siegmann.epublib.domain.Book;
import nl.siegmann.epublib.domain.Date;
import nl.siegmann.epublib.domain.Identifier;
import nl.siegmann.epublib.domain.Metadata;
import nl.siegmann.epublib.domain.Resource;
import nl.siegmann.epublib.epub.EpubWriter;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
import org.apache.pdfbox.util.PDFTextStripper;

import com.adobe.epubcheck.api.EpubCheck;
import com.adobe.epubcheck.util.EPUBVersion;
import com.nsoft.pdfrender.PdfParagraph.FontType;

@SuppressWarnings("unchecked")
public class PdfUtils {
	
	static String parentDirPath="";
	static String tempDirName="";
	static String tempDirPath="";
	static String xhtmlName="";
	static String xhtmlFileName="";
	static String xhtmlFilePath="";
	static PdfImage coverImage;
	static List<PdfImage> bookImages=new ArrayList<PdfImage>();
	
	static boolean isStartTag=false;
	
	public static String getParentDirPath(){
		return parentDirPath;
	}
	
	public static String getTempDirName(){
		return tempDirName;
	}
	
	public static String getTempDirPath(){
		return tempDirPath;
	}
	
	public static void convertToEpub(File pdfFile) throws IOException {
		// ========== CONVERT TO XHTML ========== 
		convertToXhtml(pdfFile);
		
		// ========== CONVERT TO EPUB ========== 
		parseToEpub(pdfFile);
		
		// ========== CLEAN THE TEMP FOLDER ========== 
		Utils.delete(new File(tempDirPath));
	}
	
	private static void parseToEpub(File txtFile) throws IOException {
		String fileNameNoExt=txtFile.getName().substring(0,txtFile.getName().length()-4);
		
		// ========== CONVERT TO EPUB ========== 
		Book epubBook=new Book();
		
		// ========== METADATA ========== 
		// set reflowable layout
		epubBook.setBookType(1);
		Metadata meta=epubBook.getMetadata();
		// set identifier
		Identifier ident=new Identifier();
		ident.setValue(UUID.randomUUID().toString());
		meta.addIdentifier(ident);
		// set title
		String title=txtFile.getName().substring(0,txtFile.getName().length()-4);
		meta.addTitle(title);
		// set author
		meta.addAuthor(new Author(Utils.DEF_AUTHOR));
		// set date
		meta.addDate(new Date(new java.util.Date()));
		// set description
		meta.addDescription(txtFile.getName());
		// set publisher
		meta.addPublisher(Utils.DEF_AUTHOR);
		// set language
		//meta.setLanguage(Utils.DEF_LANGUAGE);
		
		// ========== BOOK COVER ========== 
		if (coverImage!=null && coverImage.getPath()!=null){
			byte[] coverImageData=Utils.readBinaryFile(coverImage.getPath());
			String coverExt=coverImage.getName().
					substring(coverImage.getName().length()-3);
			Resource coverRes=new Resource(coverImageData,"cover."+coverExt);
			coverRes.setId("cover");
			epubBook.setCoverImage(coverRes);
		}
		
		// ========== REMAIN IMAGES ========== 
		byte[] resData;
		Resource resource;
		String resId=Utils.EMPTY;
		for (PdfImage img:bookImages){
			resData=Utils.readBinaryFile(img.getPath());
			resId=Utils.IMAGE_PREFIX+img.getName().substring(0,img.getName().length()-4);
			resource=new Resource(resData,img.getName());
			resource.setId(resId);
			epubBook.addResource(resource);
		}
		
		// ========== XHTML FILES ========== 
		resData=Utils.readBinaryFile(xhtmlFilePath);
		resource=new Resource(resData,xhtmlFileName);
		resource.setId(xhtmlName);
		epubBook.addSection(xhtmlName,resource);
		
		// ========== NAV HTML FILE ========== 
		String navData=Utils.createXhtmlNav(xhtmlName,xhtmlFileName);
		resource=new Resource(navData.getBytes("UTF-8"),"nav.html");
		resource.setId("nav");
		epubBook.setNavResource(resource);
		
		// ========== GENERATE EPUB FILE ==========
		try {
			String epubOutputPath=parentDirPath+"/"+fileNameNoExt+".epub";
			EpubWriter epubWriter=new EpubWriter();
			epubWriter.write(epubBook,
				new FileOutputStream(epubOutputPath));
			
			// ========== VALIDATE EPUB ========== 
			EpubReporter reporter=new EpubReporter();
			EpubCheck checker=new EpubCheck(
						new File(epubOutputPath),
						reporter,
						EPUBVersion.VERSION_3);
			
			// if the validation is failed, the epub file will be erased
			boolean isEpubValid=checker.validate();
			if (!isEpubValid){
				Utils.delete(new File(epubOutputPath));
				throw new IOException(reporter.getErrorMessage());
			}
			
		}catch(IOException e){
			throw e;
		}
	}
	
	private static void convertToXhtml(File pdfFile) throws IOException {
		String xhtml=convertToHtml(pdfFile);
		xhtmlName="ch01";
		xhtmlFileName=xhtmlName+".xhtml";
		xhtmlFilePath=tempDirPath+"/"+xhtmlFileName;
		Utils.saveToFile(xhtmlFilePath,xhtml);
	}
	
	private static String convertToHtml(File pdfFile) throws IOException {
		// create the temporary folder to store temporary files
		// like images, css, ... for generating EPUB
		createTempFolder(pdfFile);
		
		PDDocument doc=PDDocument.load(pdfFile);
		PDDocumentInformation pdfInfo=doc.getDocumentInformation();
		StringBuffer buff=new StringBuffer();
		
		// attach the definition and header of the html document
		buff.append(Utils.getPageHeader(pdfInfo.getTitle()));
		
		PDDocumentCatalog pdfCatalog=doc.getDocumentCatalog();
		List<PDPage> pages=pdfCatalog.getAllPages();
		System.out.print("Number of pages: "+pages.size());
		
		PDPage page;
		PdfImage[] imageList;
		String[] textList;
		
		PdfParagraph pBuff=new PdfParagraph();
		//pBuff.append(PARAGRAPH_START);
		boolean coverFound=false;
		for (int i=0;i<pages.size();i++){
			page=pages.get(i);
			// add images to page
			imageList=getImages(page,i);
			if (!coverFound && imageList.length>0){
				// add the first image as the cover image
				coverImage=imageList[0];
				// image cover is found, no longer search
				coverFound=!coverFound;
			}
			for (int j=0;j<imageList.length;j++){
				buff.append(imageList[j].getImageTag()+Utils.LINEBREAK);
				bookImages.add(imageList[j]);
			}
			
			textList=getText(doc,i);
			// only check with text-available page
			if (!isNoneTextPage(textList)){
				for (int j=0;j<textList.length;j++){
					// assume the start tag "<" always come as the first
					// character of paragraph
					if (!isStartTag){
						isStartTag=isLineStartOfTag(pBuff,textList[j]);
						if (isStartTag)
							pBuff.setFontType(FontType.BOLD);
					}
					pBuff.append(Utils.cleanToXhmtl(textList[j]));
					// check if the text line is the end of paragraph
					// if yes, then close the paragraph and attach it to 
					// the main content
					if (isEndParagraph(textList[j])){
						//pBuff.append(PARAGRAPH_END);
						buff.append(pBuff.getHtml()+Utils.LINEBREAK);
						
						pBuff=new PdfParagraph();
						//pBuff.append(PARAGRAPH_START);
					}
				}
			}
		}
		
		// attach the footer of html document
		buff.append(Utils.getPageFooter());
		
		doc.close();
		return buff.toString();
	}
	
	/**
	 * check if the line is the beginning of tag,
	 * also beginning of paragraph
	 * 
	 * @param bBuff
	 * @param text
	 * @return
	 */
	private static boolean isLineStartOfTag(PdfParagraph bBuff,String text){
		String tmpTextStr=text.trim();
		return bBuff.getText().trim().equals(Utils.EMPTY) &&
				tmpTextStr.startsWith("<") && 
				!tmpTextStr.contains(">");
	}
	
	
	/**
	 * check if a page has any text on it.
	 * 
	 * @param textList
	 * @return
	 */
	private static boolean isNoneTextPage(String[] textList){
		String tmpStr=Utils.EMPTY;
		for (String txt:textList){
			tmpStr+=txt.trim();
		}
		return tmpStr.equals(Utils.EMPTY);
	}
	
	/**
	 * check when to end the current paragraph. using some special characters
	 * at the end of line to make decision.
	 * 
	 * @param line
	 * @return
	 */
	private static boolean isEndParagraph(String line){
		String tmpLine=line.trim();
		
		// inside the tag, the paragraph should not be broken
		// until it is closed by ">" character.
		if (isStartTag){
			if (!tmpLine.endsWith(">")){
				return false;
			}else{
				// tag is only closed when it meets the ">" character
				isStartTag=false;
				return true;
			}
		}
		
		return (tmpLine.endsWith(".") || 
				tmpLine.endsWith("”") || 
				tmpLine.endsWith("\"") || 
				tmpLine.endsWith("!") ||
				tmpLine.endsWith("?") ||
				tmpLine.endsWith(">") ||
				tmpLine.endsWith(")") ||
				tmpLine.endsWith("]") ||
				tmpLine.endsWith("’"));
	}
	
	private static String[] getText(PDDocument doc,int pIdx) throws IOException {
		//ArrayList<String> listVal=new ArrayList<String>();
		
		PDFTextStripper textstrip=new PDFTextStripper("UTF-8");
		textstrip.setStartPage(pIdx);
		textstrip.setEndPage(pIdx);
		String[] res=textstrip.getText(doc).split(Utils.LINEBREAK);
		
		return res;
		//return listVal.toArray(new String[]{});
	}
	
	private static PdfImage[] getImages(PDPage page,int pIdx) throws IOException {
		ArrayList<PdfImage> outImgList=new ArrayList<PdfImage>();
		Map<String,PDXObjectImage> imageList=page.getResources().getImages();
		PDXObjectImage imgObj;
		int cnt=1;
		String imgTempName,imgFullTempName=Utils.EMPTY,
			imgTempPath,imgFullTempPath=Utils.EMPTY;
		for (String k:imageList.keySet()){
			imgObj=imageList.get(k);
			imgTempName=pIdx+"_"+cnt;
			//imgFullTempName=imgTempName+".jpg";
			imgTempPath=tempDirPath+"/"+imgTempName;
			//imgFullTempPath=tempDirPath+"/"+imgFullTempName;
			imgObj.write2file(imgTempPath);
			
			for (int i=0;i<Utils.IMAGE_EXTENSIONS.length;i++){
				//imgFullTempName=imgTempName+"."+Utils.IMAGE_EXTENSIONS[i];
				imgFullTempPath=tempDirPath+"/"+imgTempName+"."+Utils.IMAGE_EXTENSIONS[i];
				if (new File(imgFullTempPath).exists()){
					imgFullTempName=imgTempName+"."+Utils.IMAGE_EXTENSIONS[i];
					break;
				}
			}
			
			if (!imgFullTempName.equals(Utils.EMPTY)){
				outImgList.add(new PdfImage(imgFullTempName,imgFullTempPath,
							imgObj.getWidth(),imgObj.getHeight()));
			}
			
			cnt++;
		}
		return outImgList.toArray(new PdfImage[]{});
	}
		
	private static void createTempFolder(File pdfFile) throws IOException {
		parentDirPath=pdfFile.getParent();
		
		String fileName=pdfFile.getName();
		tempDirName=fileName.substring(0,fileName.length()-4)+"_tmp";
		tempDirPath=parentDirPath+"/"+tempDirName;
		
		// remove the temp directory
		File fTmpDir=new File(tempDirPath);
		if (fTmpDir.exists()){
			Utils.delete(fTmpDir);
		}
		
		// remove the epub file
		File epubFile=new File(parentDirPath+"/"+
					fileName.substring(0,fileName.length()-4)+".epub");
		if (epubFile.exists()){
			Utils.delete(epubFile);
		}
		
		new File(tempDirPath).mkdir();
	}
}
